#ifndef PAINTERLY_PLUGIN_H
#define PAINTERLY_PLUGIN_H

#include <lwserver.h>
#include <lwfilter.h>
#include <lwmonitor.h>
#include <lwcustobj.h>
#include <lwhost.h>
#include <lwimage.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "mt_random.h"

// --- makros -----------------------------------------------------------------

#define VERSION_MAJOR 0
#define VERSION_MINOR 3

#if _DEBUG
#define PLUGIN_NAME    "BrushWave_Debug"
#define PLUGIN_CO_NAME "BrushWaveObject_Debug"
#else
#define PLUGIN_NAME    "BrushWave"
#define PLUGIN_CO_NAME "BrushWaveObject"
#endif

#define PI                     3.14159265358979323846
#define MAX_NUM_OBJECTS     1000
#define MAX_FILENAME_LENGTH  256
#define MAX_NUM_TEXTURES     128

#define TRUE  1
#define FALSE 0

#define DEFAULT_BRUSH "default"

// --- data types -------------------------------------------------------------

typedef int BOOL;

struct st_Particle;
typedef struct st_Particle Particle;
typedef double MAT4[4][4];

typedef struct st_TextureInfo
{
    BOOL isLoaded;
    char filename[MAX_FILENAME_LENGTH];
    unsigned int glTextureID;
    LWImageID lwImageID;
    int width;
    int height;
} TextureInfo;

struct st_Particle {
    Particle* next;
    float  pos[3];
    float  normal[3];
    float  size;
    double zDist;
    float  colorDisplacement;
    float  colour[4]; 
    float  aspectRatio;
    BOOL   align;
    TextureInfo* textureInfo;
};

typedef void (*IterateFunc) (Particle* node, int id, void* data);

typedef struct st_PolygonInfo
{
    int numPoints;
    double** points;
    double*  normal;
} PolygonInfo;

typedef struct st_RenderSettings
{
    float partSize;
    float colorVariation;
    float colorDisplacement;
    float partDensity;
    BOOL fixedPartPlacement;
    BOOL alignParticles;
    BOOL backFaceCulling;
    char brushFilename[MAX_FILENAME_LENGTH];

} RenderSettings;

typedef struct st_BWInstanceData
{
    BOOL renderParticlesOnly;
    BOOL writeTrace;

    const LWFilterAccess* filterAccess; // access to lightwave filter info
    GlobalFunc* globalFunc;
    
    Particle* particleList;             // linked list of all particles
    float* pixelBuffer;                 // pixel data from first renderstep
    float* depthBuffer;                 // z-Buffer from first renderstep        
    int numProcessedTriangles;          // helpers
    BOOL projMatrixDirty;  
    TextureInfo textureInfos[MAX_NUM_TEXTURES];

} BWInstanceData;

typedef struct st_BWcoInstanceData
{
    LWItemID itemID;
    RenderSettings renderSettings;
    GlobalFunc* globalFunc; 

} BWcoInstanceData;

// --- globals ----------------------------------------------------------------

BWcoInstanceData** bwCoInstances;

// --- function declarations --------------------------------------------------

// --- from plugin.c ----------------------------------------------------------
void AddToGlobalList(BWcoInstanceData* instance);
void RemoveFromGlobalList(LWItemID itemID);
BWcoInstanceData* GetFromGlobalList(LWItemID itemID);

// --- from plugin_imagefilter.c ----------------------------------------------
XCALL_(int) BWActivate (long version, GlobalFunc *globalFunc, 
                        LWImageFilterHandler *local, void *serverData);
XCALL_(int) BWUiActivate (long version, GlobalFunc *globalFunc, 
                          LWInterface *ui, void *serverData);

// --- from plugin_customobject.c ---------------------------------------------
XCALL_(int) BWcoActivate (long version, GlobalFunc *globalFunc, 
                          LWCustomObjHandler *local, void *serverData);
XCALL_(int) BWcoUiActivate (long version, GlobalFunc *globalFunc, 
                            LWInterface *ui, void *serverData);

// --- from file_io.c ---------------------------------------------------------
XCALL_(LWError) BWLoad(BWInstanceData *instance, const LWLoadState *ls);
XCALL_(LWError) BWSave(BWInstanceData *instance, const LWSaveState *ss);
XCALL_(LWError) BWcoLoad(BWcoInstanceData *instance, const LWLoadState *ls);
XCALL_(LWError) BWcoSave(BWcoInstanceData *instance, const LWSaveState *ss);

// --- from create_particles.c ------------------------------------------------

void ProcessScene(BWInstanceData* instance);
void ConvertToScreenCoords(BWInstanceData* instance, 
                           const double* worldCoords, double* screenCoords);
void ConvertMatrix(const MAT4 in, double out[16]);
void SetupGLMatrices(GlobalFunc* globalFunc, int width, int height);

// --- from draw_particles.c --------------------------------------------------

BOOL PrepareBuffers(BWInstanceData* instance);
void ReleaseBuffers(BWInstanceData* instance);
void DrawParticles(BWInstanceData* instance);
void DrawPixels(int width, int height, unsigned int format, const float* pixels);
void ReadPixels(int x, int y, int width, int height, unsigned int format, float* pixels);

// --- from particle_list.c ---------------------------------------------------

Particle* CreateParticle(const float pos[3], double zDist, float size, 
                         BOOL align);
Particle* InsertOrdered(Particle* list, Particle* node, int coordinate, 
                        BOOL ascending);
Particle* InsertFront(Particle* list, Particle* node);
Particle* InsertEnd(Particle* list, Particle* node);
Particle* SortList(Particle *list, BOOL ascending);

int ClearList(Particle* list);
int Iterate(Particle* list, IterateFunc iterateFunc, void* data);

void Insert(Particle* list, Particle* newNode);
void PrintParticle(Particle* node, int id, void* data);
void DeleteParticle(Particle* node, int id, void* data);

// --- from pbuffer.c ---------------------------------------------------------

BOOL InitGlew(BWInstanceData* instance);
BOOL CreatePBuffer(int width, int height);
void DestroyPBuffer();
void ActivatePBuffer();
void DeactivatePBuffer();

// --- from gui.c -------------------------------------------------------------

LWXPanelID BWcoCreatePanel(BWcoInstanceData* instance);
LWXPanelID BWCreatePanel(BWInstanceData* instance);

// --- from texture.c ---------------------------------------------------------

TextureInfo* GetTextureInfo(BWInstanceData* instance, const char* filename);
void LoadTexture(BWInstanceData* instance, TextureInfo* textureInfo);
void ReleaseTextures(BWInstanceData* instance);

// --- from trace.c -----------------------------------------------------------

void EnableTrace(BOOL enable);
void OpenTrace();
void CloseTrace();
void WriteTrace(const char* format, ...);

// --- from utils.c -----------------------------------------------------------

void incrementLWMonitor(LWMonitor* monitor, BOOL handlePBuffer);
void strReplace(char* input, int maxLen, const char* toFind, 
                const char* toInsert);
BOOL checkGLerror();
void clearGLerrors();
double transform(const double pt[3], MAT4 t, double tpt[3]);
double dot(const double a[3], const double b[3]);
double magnitude(const double vector[3]);
BOOL normalize(double result[3], const double v[3]);
double calcTriangleArea2D(const double* a, const double* b, const double* c);
double calcTriangleArea3D (const double* a, const double* b, const double* c);
void cross(double result[3], const double a[3], const double b[3]);
void subtract(double result[3], const double a[3], const double b[3]);
void add(double result[3], const double a[3], const double b[3]);
void multiply(double result[3], const double v[3], double scalar);
BOOL isSameSide(const double p1[3], const double p2[3],
                const double a[3], const double b[3]);
BOOL isPointInTriangle(const double p[3], const double a[3], 
                       const double b[3], const double c[3]);
double rad2deg(double rad);
double deg2rad(double deg);

const LWItemInfo* getLWItemInfo(GlobalFunc* global);
const LWSceneInfo* getLWSceneInfo(GlobalFunc* global);
const LWObjectInfo* getLWObjectInfo(GlobalFunc* global);
const LWCameraInfo* getLWCameraInfo(GlobalFunc* global);
const LWTimeInfo* getLWTimeInfo(GlobalFunc* global);
const LWXPanelFuncs* getLWPanelFuncs(GlobalFunc* global);
const LWImageList* getLWImageList(GlobalFunc* global);
const char* getLWDirectory(GlobalFunc* global, const char* dirType);

enum 
{
    LWMsgType_Info,
    LWMsgType_Warning,
    LWMsgType_Error
};

void showLWMessage(GlobalFunc* global, int lwMsgType, const char* line1, const char* line2);

#endif